home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / MenuSelectionManager.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  14.0 KB  |  445 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)MenuSelectionManager.java    1.16 98/08/26
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package javax.swing;
  15.  
  16. import java.awt.*;
  17. import java.util.*;
  18. import java.awt.event.*;
  19. import javax.swing.event.*;
  20.  
  21. /**
  22.  * A MenuSelectionManager owns the selection in menu hierarchy.
  23.  * 
  24.  * @version 1.16 08/26/98
  25.  * @author Arnaud Weber
  26.  */
  27. public class MenuSelectionManager {
  28.     private static final MenuSelectionManager instance = 
  29.         new MenuSelectionManager();
  30.  
  31.     Vector selection = new Vector();
  32.  
  33.     /**
  34.      * Returns the default menu selection manager.
  35.      *
  36.      * @return a MenuSelectionManager object
  37.      */
  38.     public static MenuSelectionManager defaultManager() {    
  39.         return instance;
  40.     }
  41.     
  42.     /**
  43.      * Only one ChangeEvent is needed per button model instance since the
  44.      * event's only state is the source property.  The source of events
  45.      * generated is always "this".
  46.      */
  47.     protected transient ChangeEvent changeEvent = null;
  48.     protected EventListenerList listenerList = new EventListenerList();
  49.  
  50.     /**
  51.      * Change the selection in the menu hierarchy.
  52.      *
  53.      * @param path  an array of MenuElement objects specifying the selected path
  54.      */
  55.     public void setSelectedPath(MenuElement[] path) {
  56.         int i,c;
  57.         int currentSelectionCount = selection.size();
  58.         int firstDifference = 0;
  59.  
  60.         if(path == null) {
  61.             path = new MenuElement[0];
  62.         }
  63.  
  64.         for(i=0,c=path.length;i<c;i++) {
  65.             if(i < currentSelectionCount && (MenuElement)selection.elementAt(i) == path[i]) 
  66.                 firstDifference++;
  67.             else
  68.                 break;
  69.         }
  70.  
  71.         for(i=currentSelectionCount - 1 ; i >= firstDifference ; i--) {
  72.             ((MenuElement)selection.elementAt(i)).menuSelectionChanged(false);
  73.             selection.removeElementAt(i);
  74.         }
  75.  
  76.         for(i = firstDifference, c = path.length ; i < c ; i++) {
  77.         if (path[i] != null) {
  78.         path[i].menuSelectionChanged(true);
  79.         selection.addElement(path[i]);
  80.         } else {
  81.         /* Corner case where everything on the menu is disabled!
  82.            System.out.println("Null encountered in MenuElement Array at element " + i + " of " + c);
  83.            Thread.dumpStack();
  84.            */
  85.         }
  86.         }
  87.  
  88.     fireStateChanged();
  89.     }
  90.  
  91.     /**
  92.      * Returns the path to the currently selected menu item
  93.      *
  94.      * @return an array of MenuElement objects representing the selected path
  95.      */
  96.     public MenuElement[] getSelectedPath() {
  97.         MenuElement res[] = new MenuElement[selection.size()];
  98.         int i,c;
  99.         for(i=0,c=selection.size();i<c;i++) 
  100.             res[i] = (MenuElement) selection.elementAt(i);
  101.         return res;
  102.     }
  103.  
  104.     /**
  105.      * Tell the menu selection to close and unselect all the menu components. Call this method
  106.      * when a choice has been made
  107.      */
  108.     public void clearSelectedPath() {
  109.         setSelectedPath(null);
  110.     }
  111.  
  112.     /**
  113.      * Adds a ChangeListener to the button.
  114.      *
  115.      * @param l the listener to add
  116.      */
  117.     public void addChangeListener(ChangeListener l) {
  118.         listenerList.add(ChangeListener.class, l);
  119.     }
  120.     
  121.     /**
  122.      * Removes a ChangeListener from the button.
  123.      *
  124.      * @param l the listener to remove
  125.      */
  126.     public void removeChangeListener(ChangeListener l) {
  127.         listenerList.remove(ChangeListener.class, l);
  128.     }
  129.  
  130.     /*
  131.      * Notify all listeners that have registered interest for
  132.      * notification on this event type.  The event instance 
  133.      * is lazily created using the parameters passed into 
  134.      * the fire method.
  135.      *
  136.      * @see EventListenerList
  137.      */
  138.     protected void fireStateChanged() {
  139.         // Guaranteed to return a non-null array
  140.         Object[] listeners = listenerList.getListenerList();
  141.         // Process the listeners last to first, notifying
  142.         // those that are interested in this event
  143.         for (int i = listeners.length-2; i>=0; i-=2) {
  144.             if (listeners[i]==ChangeListener.class) {
  145.                 // Lazily create the event:
  146.                 if (changeEvent == null)
  147.                     changeEvent = new ChangeEvent(this);
  148.                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
  149.             }          
  150.         }
  151.     }   
  152.  
  153.     /**
  154.      * When a MenuElement receives an event from a MouseListener, it should never process the event
  155.      * directly. Instead all MenuElements should call this method with the event.
  156.      *
  157.      * @param event  a MouseEvent object
  158.      */
  159.     public void processMouseEvent(MouseEvent event) {
  160.         int screenX,screenY;
  161.         Point p;
  162.         int i,c,j,d;
  163.         Component mc;
  164.         Rectangle r2;
  165.         int cWidth,cHeight;
  166.         MenuElement menuElement;
  167.         MenuElement subElements[];
  168.         MenuElement path[];
  169.         Vector tmp;
  170.         int selectionSize;
  171.         p = event.getPoint();
  172.     
  173.     Component source = (Component)event.getSource();
  174.  
  175.     // System.out.println("Entered processMouseEvent");
  176.     if (!source.isShowing()) {
  177.          System.err.println("Received a mouse event from a non-showing Component " + source);
  178.         // Thread.dumpStack();
  179.         return;
  180.     }
  181.  
  182.         SwingUtilities.convertPointToScreen(p,source);
  183.  
  184.         screenX = p.x;
  185.         screenY = p.y;
  186.         //System.out.println("MouseEvent is " +event + " scr loc " + new Point(screenX,screenY));
  187.         tmp = (Vector)selection.clone();
  188.         selectionSize = tmp.size();
  189.     boolean success = false;
  190.     for (i=selectionSize - 1;i >= 0 && success == false; i--) {
  191.             menuElement = (MenuElement) tmp.elementAt(i);
  192.             subElements = menuElement.getSubElements();
  193.             
  194.             path = null;
  195.         for (j = 0, d = subElements.length;j < d && success == false; j++) {
  196.         // System.out.println("Process j loop: " + j);
  197.         if (subElements[j] == null)
  198.             continue;
  199.                 mc = subElements[j].getComponent();
  200.                 if(!mc.isShowing())
  201.                     continue;
  202.                 if(mc instanceof JComponent) {
  203.                     cWidth  = ((JComponent)mc).getWidth();
  204.                     cHeight = ((JComponent)mc).getHeight();
  205.                 } else {
  206.                     r2 = mc.getBounds();
  207.                     cWidth  = r2.width;
  208.                     cHeight = r2.height;
  209.                 }
  210.                 p.x = screenX;
  211.                 p.y = screenY;
  212.                 SwingUtilities.convertPointFromScreen(p,mc);
  213.  
  214.                 /** Send the event to visible menu element if menu element currently in
  215.                  *  the selected path or contains the event location
  216.                  */
  217.                 if(
  218.                    (p.x >= 0 && p.x < cWidth && p.y >= 0 && p.y < cHeight)) {
  219.                     int k;
  220.                     if(path == null) {
  221.                         path = new MenuElement[i+2];
  222.                         for(k=0;k<=i;k++)
  223.                             path[k] = (MenuElement)tmp.elementAt(k);
  224.                     }
  225.                     path[i+1] = subElements[j];
  226.             MenuElement currentSelection[] = getSelectedPath();
  227.  
  228.             // Enter/exit detection -- needs tuning...
  229.             if (currentSelection[currentSelection.length-1] !=
  230.             path[i+1] &&
  231.             currentSelection[currentSelection.length-2] !=
  232.             path[i+1]) {
  233.             Component oldMC = currentSelection[currentSelection.length-1].getComponent();
  234.             /*
  235.               if (oldMC instanceof JMenuItem) {
  236.                 System.out.println("Exited " + ((JMenuItem)oldMC).getText());
  237.             } else {  
  238.                 System.out.println("Exit something"); 
  239.             }
  240.             */
  241.             MouseEvent exitEvent = new MouseEvent(oldMC, MouseEvent.MOUSE_EXITED,
  242.                                   event.getWhen(),
  243.                                   event.getModifiers(), p.x, p.y,
  244.                                   event.getClickCount(),
  245.                                   event.isPopupTrigger());
  246.             currentSelection[currentSelection.length-1].
  247.                 processMouseEvent(exitEvent, path, this);
  248.             /*
  249.               if (mc instanceof JMenuItem) {
  250.               System.out.println("Entered " + ((JMenuItem)mc).getText());
  251.               } else {
  252.               System.out.println("Entered something"); 
  253.               }
  254.               */
  255.             MouseEvent enterEvent = new MouseEvent(mc, 
  256.                                    MouseEvent.MOUSE_ENTERED,
  257.                                    event.getWhen(),
  258.                                    event.getModifiers(), p.x, p.y,
  259.                                    event.getClickCount(),
  260.                                    event.isPopupTrigger());
  261.             subElements[j].processMouseEvent(enterEvent, path, this);
  262.             } 
  263.             MouseEvent mouseEvent = new MouseEvent(mc, event.getID(),event. getWhen(),
  264.                                event.getModifiers(), p.x, p.y,
  265.                                event.getClickCount(),
  266.                                event.isPopupTrigger());
  267.             subElements[j].processMouseEvent(mouseEvent, path, this);
  268.             success = true;
  269.             event.consume();
  270.             /*
  271.               if (mc instanceof JMenuItem) {
  272.               System.out.println("Drag in " + ((JMenuItem)mc).getText());
  273.               } else {
  274.               System.out.println("Drag in something");
  275.               }
  276.               */
  277.         }
  278.             }
  279.         }
  280.     }
  281.  
  282.     private void printMenuElementArray(MenuElement path[]) {
  283.     printMenuElementArray(path, false);
  284.     }
  285.  
  286.     private void printMenuElementArray(MenuElement path[], boolean dumpStack) {
  287.     System.out.println("Path is(");
  288.     int i, j;
  289.     for(i=0,j=path.length; i<j ;i++){
  290.         for (int k=0; k<=i; k++)
  291.         System.out.print("  ");
  292.         MenuElement me = (MenuElement) path[i];
  293.         if(me instanceof JMenuItem) 
  294.         System.out.println(((JMenuItem)me).getText() + ", ");
  295.         else if (me == null)
  296.         System.out.println("NULL , ");
  297.         else
  298.         System.out.println("" + me + ", ");
  299.     }
  300.     System.out.println(")");
  301.  
  302.     if (dumpStack == true)
  303.         Thread.dumpStack();
  304.     }
  305.  
  306.     /**
  307.      * Returns the component in the currently selected path 
  308.      * which contains sourcePoint.
  309.      *
  310.      * @param source The component in whose coordinate space sourcePoint
  311.      *        is given
  312.      * @param sourcePoint The point which is being tested
  313.      * @return The component in the currently selected path which
  314.      *         contains sourcePoint (relative to the source component's 
  315.      *         coordinate space.  If sourcePoint is not inside a component
  316.      *         on the currently selected path, null is returned.
  317.      */
  318.     public Component componentForPoint(Component source, Point sourcePoint) {
  319.         int screenX,screenY;
  320.         Point p = sourcePoint;
  321.         int i,c,j,d;
  322.         Component mc;
  323.         Rectangle r2;
  324.         int cWidth,cHeight;
  325.         MenuElement menuElement;
  326.         MenuElement subElements[];
  327.         Vector tmp;
  328.         int selectionSize;
  329.  
  330.         SwingUtilities.convertPointToScreen(p,source);
  331.  
  332.         screenX = p.x;
  333.         screenY = p.y;
  334.  
  335.         tmp = (Vector)selection.clone();
  336.         selectionSize = tmp.size();
  337.         for(i=selectionSize - 1 ; i >= 0 ; i--) {
  338.             menuElement = (MenuElement) tmp.elementAt(i);
  339.             subElements = menuElement.getSubElements();
  340.             
  341.             for(j = 0, d = subElements.length ; j < d ; j++) {
  342.         if (subElements[j] == null)
  343.             continue;
  344.                 mc = subElements[j].getComponent();
  345.                 if(!mc.isShowing())
  346.                     continue;
  347.                 if(mc instanceof JComponent) {
  348.                     cWidth  = ((JComponent)mc).getWidth();
  349.                     cHeight = ((JComponent)mc).getHeight();
  350.                 } else {
  351.                     r2 = mc.getBounds();
  352.                     cWidth  = r2.width;
  353.                     cHeight = r2.height;
  354.                 }
  355.                 p.x = screenX;
  356.                 p.y = screenY;
  357.                 SwingUtilities.convertPointFromScreen(p,mc);
  358.         
  359.                 /** Return the deepest component on the selection
  360.          *  path in whose bounds the event's point occurs
  361.                  */
  362.                 if (p.x >= 0 && p.x < cWidth && p.y >= 0 && p.y < cHeight) {
  363.                     return mc;
  364.                 }
  365.             }
  366.         }
  367.     return null;
  368.     }
  369.  
  370.     /**
  371.      * When a MenuElement receives an event from a KeyListener, it should never process the event
  372.      * directly. Instead all MenuElements should call this method with the event.
  373.      *
  374.      * @param e  a KeyEvent object
  375.      */
  376.     public void processKeyEvent(KeyEvent e) {
  377.         Vector tmp;
  378.         int selectionSize;
  379.         int i,j,d;
  380.         MenuElement menuElement;
  381.         MenuElement subElements[];
  382.         MenuElement path[];
  383.         Component mc;
  384.  
  385.         tmp = (Vector)selection.clone();
  386.         selectionSize = tmp.size();
  387.         for(i=selectionSize - 1 ; i >= 0 ; i--) {
  388.             menuElement = (MenuElement) tmp.elementAt(i);
  389.             subElements = menuElement.getSubElements();
  390.             
  391.             path = null;
  392.             for(j = 0, d = subElements.length ; j < d ; j++) {
  393.         if (subElements[j] == null)
  394.             continue;
  395.                 mc = subElements[j].getComponent();
  396.                 if(!mc.isShowing())
  397.                     continue;
  398.                 if(path == null) {
  399.                     int k;
  400.                     path = new MenuElement[i+2];
  401.                     for(k=0;k<=i;k++)
  402.                         path[k] = (MenuElement)tmp.elementAt(k);
  403.                     }
  404.                 path[i+1] = subElements[j];
  405.                 subElements[j].processKeyEvent(e,path,this);
  406.                 if(e.isConsumed())
  407.                     return;
  408.             }
  409.         }
  410.     }
  411.     
  412.     /** 
  413.      * Return true if c is part of the currently used menu
  414.      */
  415.     public boolean isComponentPartOfCurrentMenu(Component c) {
  416.         if(selection.size() > 0) {
  417.             MenuElement me = (MenuElement)selection.elementAt(0);
  418.             return isComponentPartOfCurrentMenu(me,c);
  419.         } else
  420.             return false;
  421.     }
  422.  
  423.     private boolean isComponentPartOfCurrentMenu(MenuElement root,Component c) {
  424.         MenuElement children[];
  425.         int i,d;
  426.     
  427.     if (root == null)
  428.         return false;
  429.  
  430.         if(root.getComponent() == c)
  431.             return true;
  432.         else {
  433.             children = root.getSubElements();
  434.             for(i=0,d=children.length;i<d;i++) {
  435.                 if(isComponentPartOfCurrentMenu(children[i],c))
  436.                     return true;
  437.             }
  438.         }
  439.         return false;
  440.     }
  441.  
  442. }
  443.  
  444.  
  445.